-- Leviathan (c) v.v.balashoff
local domainsList = require('src/Domains/list')
local Class = require('leviathan/Class')
local Log = require('leviathan/Log')

local World = Class:derive('World')

-- TODO сделать что-то с этим?
local domainsPath = 'src/Domains'

function World:new()
    self.domains = {}
    self.world = self -- store reference to world variable
end


local function priorityCompare(e1, e2)
    return e1.priority < e2.priority
end


function World:addDomain(domain, priority)
    table.insert(self.domains, domain)
    -- sort domains by priority
    if #self.domains > 1 then
        table.sort(self.domains, priorityCompare)
    end
end

function World:deleteDomain(domain)
    local index = nil
    if type(domain) == 'string' then
        domain = self:getDomain(domain)
    end
    for i = 1, #self.domains do
        if (self.domains[i] == domain) then
            index = i
        end
    end
    Log:debug('[World] Deleting domain «'..domain.name..'»...')
    -- TODO добавить корректное удаление domain
    -- а не простое удаление элемента из таблицы world
    if not index then
        Log:debug('[World] Can not delete domain «'..domain..'»')
    else
        table.remove(self.domains, index)
        table.sort(self.domains, priorityCompare)
    end
end

function World:draw()
    for i = 1, #self.domains do
        self.domains[i]:draw()
    end
end

-- TODO DEBUG
function World:dump()
    local tab = 8
    local max = 0
    -- calculate max name length
    for _, s in pairs(self.domains) do
        if #s.name > max then
            max = #s.name
        end
    end
    local s = ''
    for i = 1, max -4 + 1 do  -- substract string 'name' length, adding 1 space
        s = s..' '
    end
    print('===========================')
    print('  \27[32mDomains: \27[0m')
    print('\27[33m#', 'prior', 'name'..s..'enabled', 'show', 'address\27[0m')
    for k, domain in pairs(self.domains) do
        local n = domain.name..' '
        if #n < max then
            for i = #n, max do
                n = n..' '
            end
        end
        local en = 'false'
        if domain.enabled then
            en = 'true'
        end
        n = n..en
        print(k, domain.priority, n, '', domain.show, domain)
    end
    print('===========================')
end

function World:getManager(mgrName)
    local r = self.mgr[mgrName]
    return r
end

function World:getDomain(name)
    local domain = nil
    -- check if domain already created
    for i = 1, #self.domains do
        if self.domains[i].name == name then
            domain = self.domains[i]
        end
    end
    if not domain then
        -- domain does not exist, creating it
        collectgarbage('collect')
        local mem = math.floor(collectgarbage('count'))
        local priority = 500
        if not domainsList[name] then
            Log:error('[World] Domain is not found in «'..domainsPath..'/list.lua»: «'..name..'».')
            os.exit()
        end
        if domainsList[name]['priority'] then
            priority = domainsList[name]['priority']
        end
        local template = domainsList[name]['template']
        local map = domainsList[name]['map']
        local path = ''
        if not map then
            path = domainsPath .. '/' .. name
        else
            print('[World] TODO HERE!')
            path = domainsPath..'/SplashScreen'
        end
        local DomainCreator = require(path)
        domain = DomainCreator(self, name, map)
        domain.priority = priority
        collectgarbage('collect')
        local mem2 = math.floor(collectgarbage('count'))
        Log:debug('[World] Domain created: «'..name..'». Memory used: '..mem2 - mem..' kB')
        self.world:addDomain(domain)
    end
    return domain
end

function World:switchDomain(from, toName, keep)
    local fromName = ''
    -- managing old domain 'from'
    if from then
        fromName = from.name
    end
    Log:debug('[World] Switching domains: «'..fromName..'» => «'..toName..'»')
    if from then
        if from.onExit then
            Log:debug('[World] Call domain «'..from.name..'» onExit...')
            from:onExit(toName)
        end
    end
    if not keep then
        if not (fromName == '') then
            world:deleteDomain(from)
        end
    end
    -- managing new domain 'toName'
    local toDomain = world:getDomain(toName)
    if toDomain.onEnter then
        Log:debug('[World] Call domain «'..toDomain.name..'» onEnter...')
        toDomain:onEnter(fromName)
    end
end

function World:update(dt)
    for _, s in pairs(self.domains) do
        s:update(dt)
    end
end

return World
